// Copyright (c) 2020 Presto Labs Pte. Ltd.
// Author: taekwon

#include "coin2/exchange/okex_common/api_util/auth_key.h"

#include <list>

#include "coin2/base/time.h"
#include "coin2/exchange/base/api_util/api_signature.h"
#include "coin2/exchange/base/api_util/json_writer.h"

std::string OkexBaseAuth::GetCurrTimeSecondsThreeDecimal() {
  std::string ts_str = std::to_string(GetCurrentTimestamp() / 1000000);
  ts_str.insert(ts_str.end() - 3, 1, '.');
  return ts_str;
}

std::string OkexBaseAuth::GetSignature(
    const std::string& secret_key,
    const std::string& address,
    const std::string& method,
    const std::string& timestamp,
    const std::string& param) {
  if (secret_key.empty()) {
    throw std::runtime_error("[okex] secret key is empty");
  }
  std::string cre = timestamp + method + address + param;
  return ApiSignatureUtil::GetSignatureByHmacSHA256(cre, secret_key);
}

std::string OkexWsAuth::CreateSignature(const std::string& api_str) const {
  if (api_str.substr(0, 2) == "v3") {
    std::list<std::string> out;
    std::string ts_str = GetCurrTimeSecondsThreeDecimal();
    std::string signa = GetSignature(key_.secret_key(), "/users/self/verify", "GET", ts_str, "");
    out.emplace_back(key_.access_key());
    out.emplace_back(key_.GetValue("passphrase").value());
    out.emplace_back(ts_str);
    out.emplace_back(signa);

    JsonWriter writer;
    writer.PutString("op", "login");
    writer.PutArray("args", out);
    return writer.ToJsonString();
  } else if (api_str.substr(0, 2) == "v5") {
    JsonWriter out;
    auto ts = GetCurrentTimestamp() / 1000000000;
    std::string ts_str = std::to_string(ts);
    std::string signa = GetSignature(key_.secret_key(), "/users/self/verify", "GET", ts_str, "");
    out.PutString("apiKey", key_.access_key());
    out.PutString("passphrase", key_.GetValue("passphrase").value());
    out.PutString("timestamp", ts_str);
    out.PutString("sign", signa);

    return fmt::format(R"({{"op": "login", "args": [{}]}})", out.ToJsonString());
  } else {
    LOG(ERROR) << "OkexWsAuth CreateSignature Unexpected api_str: " << api_str;
  }

  return "";
}

std::vector<std::pair<std::string, std::string>> OkexRestAuth::GetHeaderSet(
    const std::string& address,
    const std::string& method,
    const std::string& param) const {
  std::vector<std::pair<std::string, std::string>> out;
  std::string ts_str = GetCurrTimeSecondsThreeDecimal();
  std::string signa = GetSignature(key_.secret_key(), address, method, ts_str, param);
  out.push_back(std::make_pair("OK-ACCESS-KEY", key_.access_key()));
  out.push_back(std::make_pair("OK-ACCESS-SIGN", signa));
  out.push_back(std::make_pair("OK-ACCESS-TIMESTAMP", ts_str));
  out.push_back(std::make_pair("OK-ACCESS-PASSPHRASE", key_.GetValue("passphrase").value()));
  return out;
}

AsioHttpContext* OkexRestAuth::CreateRequestByGetWithSignature(
    const std::string& address,
    UrlParamsBuilder* builder) {
  std::string query_string = builder->ComputeQueryString();
  CHECK_THROW(builder->ComputePostBody().empty()) << "It is GET Method, No Payload";
  std::string target = address;
  if (!query_string.empty()) {
    target += "?" + query_string;
  }
  return AsioHttpContext::NewGet(host_, port_, bind_ip_, target, GetHeaderSet(target, "GET", ""));
}

AsioHttpContext* OkexRestAuth::CreateRequestByPostWithSignature(
    const std::string& address,
    UrlParamsBuilder* builder) {
  std::string query_string = builder->ComputeQueryString();
  std::string post_body = builder->ComputePostBody();
  return CreateRequestByPostWithSignature(address, query_string, post_body);
}

AsioHttpContext* OkexRestAuth::CreateRequestByPostWithSignature(
    const std::string& address,
    const std::string& query_string,
    const std::string& post_body) {
  std::string target = address;
  if (!query_string.empty()) {
    target += "?" + query_string;
  }
  return AsioHttpContext::NewPost(
      host_,
      port_,
      bind_ip_,
      target,
      GetHeaderSet(target, "POST", post_body),
      post_body);
}

AsioHttpContext* OkexRestAuth::CreateRequestByPostWithSignature(
    const std::string& address,
    const std::string& post_body) {
  return CreateRequestByPostWithSignature(address, "", post_body);
}
